package models

import (
	"bigdevops/src/common"
	"encoding/json"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"strings"
)

// 工单的流程
// 审批流
// 前置检查
// 执行发布
// 后置检查
type CicdWorkOrder struct {
	Model        // 不用每次写ID 和 createAt了
	Name  string `json:"name" validate:"required,min=1,max=500" gorm:"uniqueIndex;type:varchar(100);comment:工单名称，支持使用通配符*进行模糊搜索" `

	// D:\nyy_work\go_path\src\github.com\prometheus\prometheus\config\config.go
	// 下面是global段
	UserID           uint
	CicdDeployItemId uint   `json:"cicdDeployItemId" gorm:"comment:绑定到哪个节点的 绑定第4层级"`
	GitTag           string `json:"gitTag"`

	// 服务发布项对象
	CicdDeployItemObj *CicdDeployItem `json:"cicdDeployItemObj" gorm:"-"`
	// 等待审批--> 审批不通过 --> 审批通过 --> 等待执行 --> 执行中 --> 执行完成 --> 检查通过
	Status string `json:"status"  gorm:"comment:当前状态"`

	LastModUserId     uint   `json:"lastModUserId"`
	ApprovalStatus    string `json:"approvalStatus" gorm:"comment:审批状态"`
	ApprovalUserId    uint   `json:"approvalUserId" gorm:"comment:实际的审批人"`
	ApprovalEndTime   string `json:"approvalEndTime" gorm:"comment:审批的时间"`
	ActualFlowData    string `json:"actualFlowData"  gorm:"type:text;comment:真实执行的历史记录 json，包含 每个审批的结果;"`
	CurrentStageIndex int    `json:"currentStageId"  gorm:"comment:当前发布到哪个阶段了"`

	// 多个集群的发布状态
	// 数组类型
	// 单个对象是 每个集群的状态
	// 集群名称、状态、pod信息
	// 展示 ： 审批--> 前置检查 --> stage发布 --> prod 发布 --> 检查

	GitlabRepository string `json:"gitlabRepository" gorm:"-"`
	ImageUrl         string `json:"imageUrl" gorm:"-"`

	// 可以审批工单的人的列表 来自服务树的 研发负责人 运维负责人
	CanApprovalUserNames []string `json:"canApprovalUserNames" gorm:"-"`
	// 是否可以审批工单
	CanApproval              bool   `json:"canApproval" gorm:"-"`
	CreateUserName           string `json:"createUserName" gorm:"-"`
	ApprovalUserName         string `json:"approvalUserName" gorm:"-"`
	StatusChinese            string `json:"statusChinese" gorm:"-"`
	ApprovalStatusChinese    string `json:"approvalStatusChinese" gorm:"-"`
	Key                      string `json:"key" gorm:"-"` //给前端表格
	IsCanary                 string `json:"isCanary" gorm:"-"`
	CanaryStrategyYamlString string `json:"canaryStrategyYamlString" gorm:"-"`
	NodePath                 string `json:"nodePath" gorm:"-"` // a.b.c.d
	// 获取上一次的版本 ：获取这个发布项的上一张工单的 gitTag搞定了
	LastVersion          string `json:"lastVersion"  gorm:"-"` // a.b.c.d
	CanCreateNewOrderMsg string `json:"canCreateNewOrderMsg"  gorm:"-"`
	// 时间轴数据
	TimeLineItems []WorkOrderTimeLineItem `json:"timeLineItems" gorm:"-"`
}

func (obj *CicdWorkOrder) Create() error {
	return Db.Create(obj).Error

}
func (obj *CicdWorkOrder) DeleteOne() error {

	return Db.Select(clause.Associations).Unscoped().Delete(obj).Error

}

func (obj *CicdWorkOrder) CreateOne() error {

	return Db.Create(obj).Error

}

// 事务中的创建
// 1. 先创建工单
// 2. 再创建发布阶段
func (obj *CicdWorkOrder) TransactionCreate() error {
	return Db.Transaction(func(tx *gorm.DB) error {

		err := tx.Create(obj).Error
		if err != nil {
			return err
		}
		// 遍历创建发布阶段状态记录
		var stages []DeployStage
		err = json.Unmarshal([]byte(obj.CicdDeployItemObj.Stages), &stages)
		if err != nil {
			return err
		}
		for index, stage := range stages {
			statusObj := CicdDeployStageStatus{
				Name:            stage.Name,
				Cluster:         stage.Cluster,
				K8sAppId:        stage.K8sAppId,
				Replicas:        stage.Replicas,
				StageIndex:      index + 1,
				StageCount:      len(stages),
				CicdWorkOrderId: obj.ID,
				Status:          common.CICD_STAGE_DEPLOY_PENDING,
				// 默认都是没结束
				Finished: common.GORM_ENBALE_RES_NO,
			}
			err = statusObj.CreateOne()
			if err != nil {
				return err
			}
		}
		return nil

	})
}

func (obj *CicdWorkOrder) UpdateOne() error {
	return Db.Debug().Where("id = ?", obj.ID).Updates(obj).Error

}

func (obj *CicdWorkOrder) GenMapFromKvs(kvs []string) map[string]string {

	labelsM := map[string]string{}
	for _, i := range kvs {
		kvs := strings.Split(i, "=")
		if len(kvs) != 2 {
			continue
		}
		k := kvs[0]
		v := kvs[1]
		labelsM[k] = v
	}
	return labelsM

}

// 0值替换 前转后
func (obj *CicdWorkOrder) FillDefaultData() (err error) {
	return err
}

func (obj *CicdWorkOrder) JudgeCanCreateNewOrder() {

	// 获取上一次的版本 LastVersion
	dbLastOrder, _ := GetLastCicdWorkOrderByItemId(obj.CicdDeployItemId, obj.ID)
	if dbLastOrder != nil {
		obj.LastVersion = dbLastOrder.GitTag
		// 工单还没结束不能创建新的
		if dbLastOrder.Status != common.WorkOrderInstanceStatusFinished {
			obj.CanCreateNewOrderMsg = fmt.Sprintf("上一张工单:%v 未结束不能创建新的工单", dbLastOrder.ID)
		}
	}

}

func (obj *CicdWorkOrder) FillFrontAllData() (err error) {
	dbUser, _ := GetUserById(int(obj.UserID))
	if dbUser != nil {
		obj.CreateUserName = dbUser.Username
	}
	// 设置实际审批人
	if obj.ApprovalUserId > 0 {
		dbUserApproval, err := GetUserById(int(obj.ApprovalUserId))
		if err != nil {
			return err
		}
		if dbUser != nil {
			obj.ApprovalUserName = dbUserApproval.Username
		}

	}
	// 获取所有发布阶段
	dbStages, err := GetCicdDeployStageStatusByOrderId(int(obj.ID))
	if err != nil {
		return err
	}

	dbItem, err := GetCicdDeployItemById(int(obj.CicdDeployItemId))
	if err != nil {
		return err
	}

	obj.CicdDeployItemObj = dbItem
	obj.CreatedTime = common.TimeFormat(obj.CreatedAt)
	obj.UpdatedTime = common.TimeFormat(obj.UpdatedAt)
	obj.UpdatedTime = common.TimeFormat(obj.UpdatedAt)
	obj.CicdDeployItemObj.FillFrontAllData()
	obj.GitlabRepository = obj.CicdDeployItemObj.GitlabRepository
	obj.ImageUrl = fmt.Sprintf("%s:%s", obj.CicdDeployItemObj.ImagePrefix, obj.GitTag)

	m := map[bool]string{true: "是", false: "否"}

	obj.IsCanary = m[obj.CicdDeployItemObj.IsCanary]
	obj.CanaryStrategyYamlString = obj.CicdDeployItemObj.CanaryStrategyYamlString
	// 生成可审批人列表
	obj.CanApprovalUserNames = obj.CicdDeployItemObj.StreeNodeObj.OpsRdAdmins

	obj.NodePath = obj.CicdDeployItemObj.NodePath
	obj.StatusChinese = common.WorkOrderInstanceStatus_MAP[obj.Status]
	obj.ApprovalStatusChinese = common.WorkOrderInstanceStatus_MAP[obj.ApprovalStatus]
	// 时间轴数据
	//var actualFlowData []FlowNode
	//json.Unmarshal([]byte(obj.ActualFlowData), &actualFlowData)
	// 固定第一层就是新建工单
	// 固定第二层就是审批的结果
	// 如果要展示审批的结果，需要哪些字段：审批人、审批时间、审批结果

	approvalNodeColor := "red"
	approvalNodeClass := ""
	approvalNodeDisabled := true

	// 说明有人审批了
	if obj.ApprovalStatus != common.WorkOrderInstanceStatusPendingApproval {
		approvalNodeColor = "green"
		approvalNodeClass = common.CICD_ORDER_STAGE_FRONT_CLASS
		approvalNodeDisabled = false
	}

	timeLineItems := []WorkOrderTimeLineItem{
		{
			TimeAndNodeName: fmt.Sprintf("%v 新建工单",
				obj.CreatedAt.Format("2006-01-02 15:04:05"),
			),
			DefineUserOrGroup: fmt.Sprintf("创建人:%v", obj.CreateUserName),
			ActualUser:        fmt.Sprintf("实际执行人: %v", obj.CreateUserName),
			Color:             "green",
			OutPut:            "新建工单",
			TextAreaClass:     common.CICD_ORDER_STAGE_FRONT_CLASS,
		},
		{
			TimeAndNodeName: fmt.Sprintf("%v 审批工单",
				obj.CreatedAt.Format("2006-01-02 15:04:05"),
			),
			DefineUserOrGroup: fmt.Sprintf("候选人 %v : %v", len(obj.CanApprovalUserNames), strings.Join(obj.CanApprovalUserNames, " ")),
			ActualUser:        fmt.Sprintf("审批人: %v", obj.ApprovalUserName),
			Color:             approvalNodeColor,
			OutPut:            obj.ApprovalStatusChinese,
			TextAreaClass:     approvalNodeClass,
			Disabled:          approvalNodeDisabled,
		},
	}

	// 拼接发布阶段的数据
	for index, stage := range dbStages {
		stage := stage
		stage.FillActionUserName()
		stage.FillDetail()
		index := index
		// 判断是否已发布
		// 已发布的需要添加时间，待发布的名称就可以了
		//

		nodeName := fmt.Sprintf("[%v/%v] 发布阶段:%v",
			index+1,
			len(dbStages),
			stage.Name,
		)
		// 任何动作都要拼接上时间啊
		if stage.Status != common.CICD_STAGE_DEPLOY_PENDING {
			nodeName = fmt.Sprintf("%v %v", obj.UpdatedAt.Format("2006-01-02 15:04:05"), nodeName)
		}
		finished := false
		textAreaClass := ""
		disabled := true
		if stage.Finished == 1 {
			finished = true
			textAreaClass = common.CICD_ORDER_STAGE_FRONT_CLASS
			disabled = false
		}
		out := fmt.Sprintf("过程:%v\n状态:%v 错误信息:%v", stage.Detail, stage.StatusChinese, stage.LastError)

		oneItem := WorkOrderTimeLineItem{

			TimeAndNodeName: nodeName,
			//DefineUserOrGroup: fmt.Sprintf("候选人或组:%v", node.DefineUserOrGroup),
			ActualUser:    fmt.Sprintf("执行人: %v", stage.ActionUserName),
			OutPut:        out,
			Color:         common.BOOL_COLLOR_MAP[finished],
			TextAreaClass: textAreaClass,
			Disabled:      disabled,
		}
		timeLineItems = append(timeLineItems, oneItem)
	}
	obj.JudgeCanCreateNewOrder()
	obj.TimeLineItems = timeLineItems
	return
}
func GetCicdWorkOrderAll() (objs []*CicdWorkOrder, err error) {
	err = Db.Find(&objs).Error
	return
}

// select * from ecs where
func GetCicdWorkOrderByIdsWithLimitOffset(ids []int, limit, offset int) (objs []*CicdWorkOrder, err error) {
	err = Db.Limit(limit).Offset(offset).Find(&objs, ids).Error
	return
}

func GetCicdWorkOrderById(id int) (*CicdWorkOrder, error) {

	var dbObj CicdWorkOrder
	err := Db.Where("id = ?", id).First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("CicdWorkOrder不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}

// 获取上一张工单
func GetLastCicdWorkOrderByItemId(itemId, thisId uint) (*CicdWorkOrder, error) {

	var dbObj CicdWorkOrder
	err := Db.Where("cicd_deploy_item_id = ? and id != ?", itemId, thisId).Order("created_at desc").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("CicdWorkOrder不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}

func GetCicdWorkOrderByName(name string) (*CicdWorkOrder, error) {

	var dbObj CicdWorkOrder
	err := Db.Where("name = ?", name).First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("CicdWorkOrder不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}
